<?php
/**
 * 浮点数计算函数封装compare
 * User: 乐杨俊
 * Date: 2018/8/8
 * Time: 下午3:12
 */

namespace app\myself;

class Bc
{
    // 金额计算方式, 增加
    const CALC_ADD = 'add';
    // 金额计算方式, 减少
    const CALC_SUB = 'sub';
    // 金额计算方式, 相除
    const CALC_DIV = 'div';
    // 金额计算方式, 相乘
    const CALC_MUL = 'mul';
    // 金额计算方式, 比较大小
    const CALC_COMP = 'comp';

    /**
     * 使用较高精度的计算方法
     * @param $amount_1
     * @param $amount_2
     * @param $precision
     * @param string $method
     * @return float|int|string
     */
    public function calculateByPrecision($amount_1, $amount_2, $precision, $method = self::CALC_ADD)
    {
        $amount = 0;

        // 如果为金额小于0且为加法, 则自动转化为减法
        if ($method == self::CALC_ADD && $amount_2 < 0) {
            $method = self::CALC_SUB;
        }

        $bc_exists = function_exists('bcadd');
        switch ($method) {
            case self::CALC_ADD :
                $amount = $bc_exists ? bcadd($amount_1, $amount_2, $precision) : round($amount_1 + $amount_2, $precision);
                break;
            case self::CALC_SUB :
                $amount = $bc_exists ? bcsub($amount_1, abs($amount_2), $precision) : round($amount_1 - abs($amount_2), $precision);
                break;
            case self::CALC_DIV :
                $amount = $bc_exists ? bcdiv($amount_1, $amount_2, $precision) : round($amount_1 / $amount_2, $precision);
                break;
            case self::CALC_MUL :
                $amount = $bc_exists ? bcmul($amount_1, $amount_2, $precision) : round($amount_1 * $amount_2, $precision);
                break;
            case self::CALC_COMP :
                // 高精度的比较, 返回1表示 $amount_1 > $amount_2
                $amount = $bc_exists ? bccomp($amount_1, $amount_2, $precision) : ($amount_1 > $amount_2 ? 1 : ($amount_1 == $amount_2 ? 0 : -1));
                break;
        }

        return $amount;
    }

    /**
     * 检测第一个数是否大于第二个数
     * @param float $amount_1
     * @param float $amount_2
     * @param int $precision
     * @return boolean
     */
    public function isGt($amount_1, $amount_2, $precision = 2)
    {
        return $this->calculateByPrecision($amount_1, $amount_2, $precision, self::CALC_COMP) === 1;
    }

    /**
     * 检测第一个数是否大于等于第二个数
     * @param float $amount_1
     * @param float $amount_2
     * @param int $precision
     * @return boolean
     */
    public function isGe($amount_1, $amount_2, $precision = 2)
    {
        return $this->calculateByPrecision($amount_1, $amount_2, $precision, self::CALC_COMP) !== -1;
    }

    /**
     * 检测两个数是否相等
     * @param float $amount_1
     * @param float $amount_2
     * @param int $precision
     * @return boolean
     */
    public function isEqual($amount_1, $amount_2, $precision = 2)
    {
        return $this->calculateByPrecision($amount_1, $amount_2, $precision, self::CALC_COMP) === 0;
    }

    /**
     * 检测$amount_1是否在$amount_2和$amount_3这个区间
     * @param $amount_1
     * @param $amount_2
     * @param $amount_3
     * @param int $precision
     * @param boolean $is_contain_critical 是否包含$amount_2和$amount_3临界点
     * @return boolean
     */
    public function isInInterval($amount_1, $amount_2, $amount_3, $precision = 2, $is_contain_critical = true)
    {
        if ($is_contain_critical) {
            if (!$this->isGe($amount_1, $amount_2, $precision)) {
                return false;
            }
            if ($this->isGt($amount_1, $amount_3, $precision)) {
                return false;
            }
            return true;
        }

        // 不包含临界点
        if (!$this->isGt($amount_1, $amount_2, $precision)) {
            return false;
        }
        if ($this->isGe($amount_1, $amount_3, $precision)) {
            return false;
        }
        return true;
    }

    /**
     * 两个数相加
     * @param float $amount_1
     * @param float $amount_2
     * @param int $precision
     * @return float
     */
    public function calcAdd($amount_1, $amount_2, $precision = 2)
    {
        return $this->calculateByPrecision($amount_1, $amount_2, $precision, self::CALC_ADD);
    }

    /**
     * 对数组中所有数值进行求和
     */
    public function calcAddArray(array $arr, $precision = 2)
    {
        $sum = '0.00';
        foreach ($arr as $val) {
            if (is_numeric($val)) {
                $sum = $this->calcAdd($sum, $val, $precision);
            }
        }

        return $sum;
    }

    /**
     * 两个数相减
     * @param float $amount_1
     * @param float $amount_2
     * @param int $precision
     * @return float
     */
    public function calcSub($amount_1, $amount_2, $precision = 2)
    {
        return $this->calculateByPrecision($amount_1, $amount_2, $precision, self::CALC_SUB);
    }

    /**
     * 两个数相乘
     * @param float $amount_1
     * @param float $amount_2
     * @param int $precision
     * @return float
     */
    public function calcMul($amount_1, $amount_2, $precision = 2)
    {
        return $this->calculateByPrecision($amount_1, $amount_2, $precision, self::CALC_MUL);
    }

    /**
     * 两个数相除
     * @param float $amount_1
     * @param float $amount_2
     * @param int $precision
     * @return float
     */
    public function calcDiv($amount_1, $amount_2, $precision = 2)
    {
        return $this->calculateByPrecision($amount_1, $amount_2, $precision, self::CALC_DIV);
    }
}